// Copyright 2012 dmitry.a.novikov@gmail.com
// 
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
// 
// http://www.apache.org/licenses/LICENSE-2.0
// 
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// CnsZ::Object declaration


#ifndef _INCLUDED_CNZ_VARIANT_H_
#define _INCLUDED_CNZ_VARIANT_H_

#include "exception.h"

namespace CnsZ {

  typedef const String& StringConstRef;
  typedef P<Object> ObjectCPtr;
  typedef P<Object> ObjectPtr;
  typedef const Object* ObjectConstPonter;
  typedef Object* ObjectPonter;

#define CNSZ_VARIANT_TYPES() \
    CNSZ_VARIANT(        Int64,             Int64) \
    CNSZ_VARIANT(       Double,            double) \
    CNSZ_VAR_OBJ(       String,const ValueString *) \
    CNSZ_VAR_PTR(    ObjectPtr,      ObjectPonter) \
    CNSZ_VAR_PTR(   ObjectCPtr, ObjectConstPonter) \

  class Variant {
  public:
    Variant() : typ(VarEmpty) {}
    ~Variant() { destructValue(); }

#define CNSZ_VARIANT(Type, ref) Var##Type##,
#define CNSZ_VAR_OBJ(Type, ref) CNSZ_VARIANT(Type, ref)
#define CNSZ_VAR_PTR(Type, ref) CNSZ_VAR_OBJ(Type, ref)

    enum Type {
      VarEmpty = 0,
      CNSZ_VARIANT_TYPES()
    };
    
    Type type() const { return typ; }

#undef CNSZ_VARIANT
#define CNSZ_VARIANT(Type, ref) \
    Variant(ref val) : typ(Var##Type##) { constructValue(); *((Type*)data)=val; }\
    void set(ref val) { destructValue(); typ=Var##Type##; constructValue(); *((Type*)data)=val; }\
    Variant& operator = (ref val) { set(val); return *this; }\
    ref as##Type##() const { if(typ!=Var##Type##) throw NotApplicable(); return *((Type*)data); }\
    operator ref() const { return as##Type##(); }

    bool operator < (const Variant& op2) const;
    bool operator > (const Variant& op2) const;
    bool operator ==(const Variant& op2) const;

    CNSZ_VARIANT_TYPES()
    static Variant Empty;
  protected:
    void constructValue();
    void destructValue();

    Type  typ;
    UInt8 data[sizeof(double)];
  };

} //namespace CnsZ

#endif //#ifndef _INCLUDED_CNZ_VARIANT_H_